﻿using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using HandyControl.Controls;
using HardToolApp.Model;
using HardToolApp.Tool;
using HardToolApp.View;
using HidLibrary;
using OxyPlot;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Linq;
using System.Management;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Forms;
using static HardToolApp.Tool.Common;
using static HardToolApp.Tool.USBMonitor;

namespace HardToolApp.ViewModel
{

    internal class USBInfoVM:ObservableObject
    {
        public class VMHelper:IComparable<VMHelper>
        {
            public string Name { set; get; }
            public Type SendModel { get; set; }
            public PeripheralVM VM { get; set; }
            public int Sort{get;set;}
            public System.Windows.Controls.UserControl Control { get; set; }
            public VMHelper(string name, Type SendModel, PeripheralVM vM, System.Windows.Controls.UserControl control, int sort)
            {
                Name = name;
                this.SendModel = SendModel;
                VM = vM;
                Control = control;
                Sort = sort;    
            }

            public int CompareTo(VMHelper helper)
            {
                if(this.Sort != helper.Sort)
                {
                    return Sort.CompareTo(helper.Sort);
                }
                return 0;
            }
        }
        public class USBDeviceCombo
        {
            public int ID { set; get; }
            public string Description { set; get; }
            public HidDevice device { set; get; }
            public USBDeviceCombo(int id,string d,HidDevice device) { this.ID = id; Description = d;this.device = device; }
        }
        SocketComm socket;
        BlockingCollection<byte[]> comSendQueue = new BlockingCollection<byte[]>();
        public BlockMap<int,object> waitMap = new BlockMap<int,object>();
        public static Hashtable peripheralList { get; set; } = new Hashtable();
        public static ObservableCollection<HandyControl.Controls.TabItem> tabItems { get; set; } = new ObservableCollection<HandyControl.Controls.TabItem>();
        private string _title = "HardTool";
        public string title { get { return _title; } set { SetProperty(ref _title, value); } }
        private USBDeviceCombo selectedModel;
        public USBDeviceCombo SelectedModel { get { return selectedModel; }set { SetProperty(ref selectedModel, value); } }
        private ObservableCollection<USBDeviceCombo> _comboBoxItems = new ObservableCollection<USBDeviceCombo>();
        public ObservableCollection<USBDeviceCombo> comboBoxItems { 
            get { return _comboBoxItems; } 
            set { SetProperty(ref _comboBoxItems, value); }
        }

        public static USBInfoVM USBVm;

        public RelayCommand<int> comboCmd { get; set; }

        public void Regsiter(string name,object control)
        {
            HandyControl.Controls.TabItem item = new HandyControl.Controls.TabItem();
            item.Header = name;
            item.Content = control;
            tabItems.Add(item);
        }
        #region 页面逻辑
        public USBInfo usbInfo { get; set; }

        public USBInfoVM()
        {
            usbInfo = new USBInfo() { connected = false};
            USBVm = this;
            comboCmd = new RelayCommand<int>(RegionChangedCmdExecuted);
            new Task(ComQueueTask).Start();
            WeakReferenceMessenger.Default.Register<ReplyClass>(this, reply);



            //tabcontrol 实例化
           var type = from t in Assembly.GetExecutingAssembly().GetTypes()
                               where t.IsClass && t.Namespace == "HardToolApp.ViewModel"
                       select t;

            List<VMHelper> helpers = new List<VMHelper>();
            foreach(Type t in type.ToList())
            {
                var classIfno = t.GetCustomAttribute<ViewModelHelper>();
                if( classIfno != null )
                {
                    System.Windows.Controls.UserControl ctl = (System.Windows.Controls.UserControl)Activator.CreateInstance(classIfno.controlType);
                    
                    if(ctl.DataContext.GetType().BaseType.Equals(typeof(PeripheralVM)) || ctl.DataContext.GetType().BaseType.Equals(typeof(CommVM)))
                    {
                        PeripheralVM vm = (PeripheralVM)(ctl.DataContext);
                        helpers.Add( new VMHelper(classIfno.HeaderName, classIfno.SendType, vm, ctl,classIfno.Sort));
                    }
                }
            }
            helpers.Sort();
            foreach (VMHelper vm in helpers)
            {
                peripheralList.Add(vm.SendModel, vm);
                Regsiter(vm.Name, vm.Control);
            }

            socket = new SocketComm();
            socket.tcpServer.DataReceived += TcpServer_DataReceived;
            title =  "HardTools-"+ System.Reflection.Assembly.GetExecutingAssembly().GetName().Version+"-TCP端口:" + socket.port.ToString();

            USBMonitor.USB monitor = new USBMonitor.USB();
            monitor.AddUSBEventWatcher(USBEventHandler, USBEventHandler, new TimeSpan(0,0,0, 0, 100));
            new Task(()=>
            {
                while(true)
                {
                    if (selectedModel != null && selectedModel.device!=null)
                    {
                        usbInfo.connected = selectedModel.device.IsConnected;
                        if(selectedModel.device.IsConnected)
                        {
                            HidReport report = selectedModel.device.ReadReport();
                            if(report.ReadStatus == HidDeviceData.ReadStatus.Success)
                            {
                                RecData(report.Data);
                            }
                        }
                        //Task.Delay(1);
                    }
                }
            }).Start();
            GetDevices();
            if(comboBoxItems.Count > 0 )
            {
                SelectDevice(0);
            }
        }

        /// <summary>
        /// tcp 接收到数据就完整转发给usb
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TcpServer_DataReceived(object sender, AsyncSocketEventArgs e)
        {
            byte[] b = e._state.recvDataBuffer;
            int len = e._state.recvLen;
            EndPoint ep = e._state.clientSocket.RemoteEndPoint;
            UsbDataSend(b);
        }

        #region usb相关
        /// <summary>
        /// 插拔检测
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void USBEventHandler(Object sender, EventArrivedEventArgs e)
        {
            if (e.NewEvent.ClassPath.ClassName == "__InstanceCreationEvent")
            {
                Console.WriteLine("USB插入时间：" + DateTime.Now + "\r\n");
                
                System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
                {
                    bool needChoose = false;
                    if (comboBoxItems.Count == 0)// 原来没有设备被选择
                    {
                        needChoose = true;
                    }
                    foreach (USBControllerDevice Device in USB.WhoUSBControllerDevice(e))
                    {
                        Console.WriteLine("\tAntecedent：" + Device.Antecedent + "\r\n");
                        Console.WriteLine("\tDependent：" + Device.Dependent + "\r\n");
                        string temp = Device.Dependent.Replace("\\\\","#").ToLower();
                    

                        foreach (HidDevice hid in HidDevices.Enumerate())
                        {
                            if (hid.DevicePath.Contains(temp))
                            {
                                USBDeviceCombo find = comboBoxItems.FirstOrDefault((t) => t.device.DevicePath.Contains(temp));
                                if (find == null)
                                {
                                    comboBoxItems.Add(new USBDeviceCombo(comboBoxItems.Count, "#" + (comboBoxItems.Count).ToString(), hid));
                                    hid.Inserted += DeviceAttachedHandler;
                                    hid.Removed += DeviceRemovedHandler;
                                }
                            }
                        }
                    }
                    if(needChoose)
                    {
                        SelectDevice(0);
                    }

                }));
            }
            else if (e.NewEvent.ClassPath.ClassName == "__InstanceDeletionEvent")
            {
                Console.WriteLine("USB拔出时间：" + DateTime.Now + "\r\n");
                foreach (USBControllerDevice Device in USB.WhoUSBControllerDevice(e))
                {
                    Console.WriteLine("\tAntecedent：" + Device.Antecedent + "\r\n");
                    Console.WriteLine("\tDependent：" + Device.Dependent + "\r\n");

                }
            }

            
        }

        public void RegionChangedCmdExecuted(int e)
        {
            SelectDevice(e);
        }

        /// <summary>
        /// 列出hid列表
        /// </summary>
        /// <returns></returns>
        public ObservableCollection<USBDeviceCombo> GetDevices()
        {
            int count = 0;
            //ObservableCollection<USBDeviceCombo> temp = new ObservableCollection<USBDeviceCombo>();
            comboBoxItems.Clear();
            foreach (HidDevice hid in HidDevices.Enumerate())
            {
                if(hid.DevicePath.Contains("vid_0483&pid_6666"))
                {
                    comboBoxItems.Add(new USBDeviceCombo(count, "#" + count.ToString(),hid));
                    hid.Inserted += DeviceAttachedHandler;
                    hid.Removed += DeviceRemovedHandler;
                    count++;
                }
            }
            return comboBoxItems;
        }

        /// <summary>
        /// 选择hid设备
        /// </summary>
        /// <param name="index"></param>
        public void SelectDevice(int index)
        {
            if(index > comboBoxItems.Count)
            {
                return;
            }
            if(SelectedModel != null)
            {
                SelectedModel.device.MonitorDeviceEvents = false;
                SelectedModel.device.CloseDevice();
            }
            SelectedModel = comboBoxItems[index];
            SelectedModel.device.OpenDevice();
            SelectedModel.device.MonitorDeviceEvents = true;
        }

        private void DeviceAttachedHandler()
        {
            Console.WriteLine("Device attached.");
        }
        private void DeviceRemovedHandler()
        {
            //SelectedModel.device.CloseDevice();
            Console.WriteLine("Device removed.");
        }
        #endregion
        #endregion


        #region 通信相关
        public void ComQueueTask()
        {
            while(true)
            {
                byte[] bytes = comSendQueue.Take();
                UsbDataSend(bytes);
                TcpDataSend(bytes, 1);
            }
           
        }
        public void UsbDataSend(byte[] b)
        {
            if (selectedModel == null || !selectedModel.device.IsConnected)
            {
                Growl.Warning("设备未连接");
            }
            else
            {
                HidReport hidReport = new HidReport(b.Length);
                hidReport.Data = b;
                selectedModel.device.WriteReport(hidReport);
            }
        }

        private void TcpDataSend(byte[] b,byte header)
        {
            byte[] data = new byte[b.Length + 1];
            b.CopyTo(data, 1);
            data[0] = header;

            
            socket.tcpServer.SendAll(data);
        }

        public void AddMap(int t,object res)
        {
            waitMap.Add(t,res);
        }
        /// <summary>
        /// 发送usb数据
        /// </summary>
        /// <param name="b"></param>
        public object send(byte[] b)
        {
            // 页面发送数据标头为1，然后全部发送
            comSendQueue.Add(b);
            return waitMap.GetValue((b[3] << 8) + b[4],1000);
        }
        public object send(byte[] b,int timeout)
        {
            // 页面发送数据标头为1，然后全部发送
            comSendQueue.Add(b);
            return waitMap.GetValue((b[3] << 8) + b[4], timeout);
        }

        public void sendNoWait(byte[] b)
        {
            comSendQueue.Add(b);
        }


        /// <summary>
        /// usb数据回复
        /// </summary>
        /// <param name="b"></param>
        public void reply(object t,ReplyClass b)
        {
            byte[] bytes = new byte[7];
            bytes[0] = 0xaa;
            bytes[1] = 0x55;
            bytes[2] = 8;
            bytes[3] = b.cmd;
            bytes[4] = b.subCmd;
            bytes[5] = b.data;
            bytes[6] = Common.chksum(bytes,6);
            USBInfoVM.USBVm.sendNoWait(bytes);
        }


        /// <summary>
        /// 接收usb数据
        /// </summary>
        /// <param name="message"></param>
        public void RecData(byte[] message)
        {
            // Console.WriteLine(BitConverter.ToString(message));
            try
            {
                if (message.Length < 6)
                {
                    return;
                }
                else
                {
                    if (!((message[0] == 0xaa && message[1] == 0x55) || (message[0] == 0x55 && message[1] == 0xaa)) || message[2] > message.Length)//data len error or header error
                    {
                        return;
                    }
                    if (message[message[2] - 1] != Common.chksum(message, message[2] - 1)) // checksum error
                    {
                        return;
                    }

                    //usb上传数据标头为2
                    TcpDataSend(message, 2);
                    WeakReferenceMessenger.Default.Send(message);
                }
            }
            catch (Exception e)
            {
            }
                
        }
        #endregion

    }
}
